#ifndef __ILIB_CORE_MSGDB_H_20120304__
#define __ILIB_CORE_MSGDB_H_20120304__

#include "../../iHeader.h"
#include "iSmartPtr.h"
#include "iAtomic.h"
#include "iAllocator.h"
#include "iSingleton.h"
#include "iLock.h"

class iMsgDB : public iAllocator::Object
{
public:
        static iMsgDB* new_instance(unsigned char* buf, unsigned int len, unsigned int offset=0, int flag=FLAG_HAS, iAllocator* alloc=iSingleton<iAllocator>::instance())
		{
			return new iMsgDB(buf, len, offset, flag, alloc);
		}

private:
	iMsgDB(unsigned char* buf, unsigned int len, unsigned int offset, int flag, iAllocator* alloc)
		:iAllocator::Object(alloc)
		,m_next_db(NULL)
		,m_read_cursor(NULL)
		,m_write_cursor(NULL)
	{
		if(NULL==buf && len)
		{
			m_data_block = iDataBlock::new_instance(NULL, len);
		}
		else if(buf)
		{
			m_data_block = iDataBlock::new_instance(buf, len, flag);
		}
		else 
		{
			return ;
		}
		m_read_cursor = m_write_cursor = m_data_block->get_buffer();
        m_write_cursor += offset;
	}

    ~iMsgDB()
    {
        if(m_next_db)
            m_next_db->release();
        m_next_db=NULL;
    }

public:
	enum { FLAG_REF, FLAG_HAS };
	class iDataBlock : public iAllocator::Object
                     , public iRefObject<iAtomInt, iNullLock>
	{
    public:
        typedef iSmartPointer<iDataBlock> sp;
        static sp new_instance(uint8_t* buf=NULL, uint32_t len=0, uint32_t flag=FLAG_HAS, iAllocator* alloc=iSingleton<iAllocator>::instance())
        {
            if(NULL==buf && len)
                buf = alloc->allocate(len);
            return new(alloc->allocate(sizeof(iDataBlock))) iDataBlock(buf, len, flag, alloc);
        }
		
		inline unsigned char* get_buffer() { return m_buffer; }
		inline unsigned int get_length() { return m_length; }
		inline void set_length(unsigned int len) { m_length = len;}
        inline void release() { m_allocator->deallocate(this); }

	protected:
        iDataBlock(uint8_t * buf, uint32_t len, uint32_t flag, iAllocator* alloc)
			:iAllocator::Object(alloc)
            ,m_buffer(buf)
			,m_length(len)
			,m_flag(flag)
		{
		}
		virtual ~iDataBlock()
		{
			if(m_flag & FLAG_HAS)
			{
				m_allocator->deallocate(m_buffer);
				m_buffer = NULL;
				m_length = 0;
			}
		}
		virtual void on_destroy()
		{
			release();
		}
	private:
		unsigned char* m_buffer;
		unsigned int   m_length;
		unsigned int   m_flag;
	};

	uint32_t read(unsigned char* buf, uint32_t len)
	{
		if(buf == NULL) return 0;
		if(m_read_cursor==NULL) return 0;

		uint32_t readcnt = 0;
		uint32_t cnt = m_data_block->get_buffer()+m_data_block->get_length()-m_read_cursor;
		cnt = cnt>len ? len:cnt;
		memcpy(buf, m_read_cursor, cnt);
		m_read_cursor += cnt;
		len -= cnt;
		readcnt += cnt;
		if(len && m_next_db)
			readcnt += m_next_db->read(buf+cnt, len);
		return readcnt;
	}
	uint32_t write(unsigned char* buf, uint32_t len)
	{
		if(buf == NULL) return 0;
		if(m_write_cursor == NULL) return 0;

		uint32_t writecnt = 0;
		uint32_t cnt = m_data_block->get_buffer()+m_data_block->get_length()-m_write_cursor;
		cnt = cnt>len ? len:cnt;
		memcpy(m_write_cursor, buf, cnt);
		m_write_cursor += cnt;
		len -= cnt;
		writecnt += cnt;
		if(len)
		{
			if(NULL == m_next_db)
				m_next_db = iMsgDB::new_instance(NULL, m_data_block->get_length(), 0, FLAG_HAS, m_allocator);
			
			writecnt += m_next_db->write(buf+cnt, len);

		}

		return writecnt;
	}
    int write(iDataBlock* db)
    {
        m_data_block = db;
        m_read_cursor = m_data_block->get_buffer();
        m_write_cursor = m_data_block->get_buffer()+m_data_block->get_length();
		return db->get_length();
    }
	void append(iMsgDB* mbs)
	{
		if(m_next_db)
			m_next_db->append(mbs);
		else
			m_next_db = mbs;
	}
    iMsgDB* duplicate()
    {
        iMsgDB* mdb = iMsgDB::new_instance(NULL, 0);         

        if(m_next_db)
            mdb->m_next_db = m_next_db->duplicate();

        mdb->m_data_block = m_data_block;
        mdb->m_read_cursor = m_read_cursor;
        mdb->m_write_cursor = m_write_cursor;
        mdb->m_allocator = m_allocator;

        return mdb;
    }
	iMsgDB* clips(uint32_t length, iMsgDB*& out)
	{
		if(length<get_data_length())
		{
			out = this->duplicate();
			out->m_read_cursor += length;
			m_write_cursor = out->m_read_cursor;
			m_next_db = NULL;
			return this;
		}
		else if(length == get_data_length())
		{
			out = NULL;
			m_next_db = NULL;
			return this;
		}
		else
		{
			iMsgDB* last = NULL;
			if(m_next_db)
				last = m_next_db->clips(length-get_data_length(), out);
			else
				return NULL;
			return last ? this : NULL;
		}
	}
	std::string dump()
	{
		std::stringstream hex;
		char mark[]="0123456789ABCDEF";
		unsigned char* p = m_read_cursor;
		while(p != m_write_cursor)
		{
			hex << mark[*p>>4];
			hex << mark[*p&0x0F];
			p++;
		}
		if(m_next_db)
			hex << m_next_db->dump();
		return hex.str();
	}
    void release()
    {
        m_allocator->deallocate(this);
    }
    inline uint32_t get_data_length() const { return m_write_cursor-m_read_cursor;}
	inline uint32_t get_total_data_length() 
	{
		uint32_t len = get_data_length();
		if(m_next_db)
			len += m_next_db->get_total_data_length();
		return len;
	}
    inline iMsgDB* next() { return m_next_db; }
	inline unsigned char* get_read_cursor() const  { return m_read_cursor; }
	inline unsigned char* get_write_cursor() const { return m_write_cursor; }

private:
	iMsgDB* m_next_db;
	iSmartPointer<iDataBlock> m_data_block;
	unsigned char* m_read_cursor;
	unsigned char* m_write_cursor;
};
#endif  //__ILIB_CORE_MSGDB_H_20120304__
